﻿// This is an open source non-commercial project. Dear PVS-Studio, please check it.
// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com

// ReSharper disable CheckNamespace
// ReSharper disable ClassNeverInstantiated.Global
// ReSharper disable CommentTypo
// ReSharper disable IdentifierTypo
// ReSharper disable InconsistentNaming
// ReSharper disable MemberCanBePrivate.Global
// ReSharper disable StringLiteralTypo
// ReSharper disable UnusedMember.Global

/* Xri.cs -- Extensible Resource Identifier.
 * Ars Magna project, http://arsmagna.ru
 */

using System;
using System.ComponentModel;
using System.IO;
using System.Text.Json.Serialization;
using System.Xml.Serialization;

using AM;
using AM.IO;
using AM.Runtime;
using AM.Text;

#nullable enable

namespace ManagedIrbis.Identifiers
{
    //
    // https://ru.wikipedia.org/wiki/XRI
    //
    // XRI (англ. Extensible Resource Identifier) - расширяемый идентификатор
    // ресурса, разработанный организацией OASIS, прежде всего, как будущая
    // замена URL в Интернете. XRI - это новая, совместимая с IRI и URI схема
    // (протокол) для создания абстрактных идентификаторов ресурсов.
    // Такие идентификаторы не зависят от платформы, домена, путей
    // и программ - они полностью абстрактны и поэтому могут быть
    // едины для всех доменов и каталогов.
    //
    // Цель идентификаторов XRI более глобальна, чем может показаться
    // на первый взгляд. Идентификаторы XRI рассматриваются как будущая
    // замена не только URL, но, возможно, и самой системе DNS
    // и даже IP-адресам в системе идентификации ресурсов Интернета
    // и Всемирной паутины. XRI имеют полную обратную совместимость
    // с DNS и IP-адресами, поэтому уже сейчас IP-адреса и имена
    // доменов DNS могут использоваться как I-Names (см. ниже)
    // в системе XRI. В целом, можно сказать, что идея XRI "абстрагирует"
    // имена доменов DNS. Как и имена доменов в DNS, идентификаторы
    // XRI могут делегироваться, вкладываться друг в друга.
    // Однако идентификаторы XRI обладают несколькими возможностями,
    // недоступными для DNS, это:
    //
    // * Неиерархическая одноранговая адресация (англ. non-hierarchical
    // peer-to-peer addressing) - когда два узла сети могут присвоить
    // друг другу идентификаторы XRI и сами взаимно разрешать их.
    //
    // * Взаимные ссылки (англ. cross-references) - когда XRI вложены
    // один в другой и, следовательно, один логический ресурс может
    // идентифицироваться в разных контекстах (похоже на современную
    // технологию cross-domain data sharing, в которой один ресурс может
    // использоваться несколькими доменами одновременно).
    //
    // * Глобальные реестры контекстов (англ. global context
    // registries) - простой, доступный человеку способ указания
    // глобального контекста XRI:
    //
    // Символ = для частных лиц
    // Символ @ для организаций и бизнеса
    // Символ + для общих понятий (например: аэропорт, дом, шкаф и т. п.)
    //
    // Ещё одно преимущество заключается в том, что ресурсы,
    // идентифицируемые XRI, недоступны для спама. Обладатель I-Name
    // в системе XRI сам определяет разрешения для доступа к ресурсу,
    // так что становится возможной упреждающая фильтрация всех
    // запросов. Это же обстоятельство, безусловно, улучшает
    // безопасность ресурсов.
    //
    // XRI — это ключевая технология в системе XDI (англ. XRI Data
    // Interchange) - системе обмена данными XRI. XDI - протокол
    // (разрабатываемый той же организацией OASIS), цель которого - создание
    // универсального формата для обмена информацией, в котором XML-данные
    // из любого источника могли бы быть точно идентифицированы,
    // классифицированы, синхронизированы и связаны с другими данными
    // (речь идёт о концепции Dataweb). Это похоже на Всемирную паутину
    // из документов HTML, но только предназначенную для компьютеров,
    // а не для людей. Это также перекликается с концепцией семантической
    // паутины, продвигаемой Консорциумом Всемирной паутины.
    //
    // Идентификаторы XRI изначально рассчитаны на использование Юникода,
    // полностью совместимы с новым форматом идентификаторов IRI,
    // и обеспечивают новый уровень равноправия языков в Интернете.
    //
    // В заключении можно отметить, что XRI - это постоянные, легко
    // портируемые, частные идентификаторы, которые могут стать
    // основой не только для паутины информации, но и для создания
    // Социальной паутины, о которой сейчас много говорят.
    //
    // Структура
    // Идентификаторы XRI имеют два слоя:
    //
    // I-Numbers — постоянные сетевые адреса (похожие на IP-адреса).
    // Такие адреса будут регистрироваться на какой-либо ресурс
    // (человека, организацию, приложение, файл, цифровой объект и т. д.)
    // и никогда больше не перерегистрироваться (в отличие от IP-адресов
    // и доменов DNS). Это означает, что идентификатор I-Number всегда
    // можно будет использовать как адрес для какого-либо ресурса
    // (по крайней мере, пока этот ресурс доступен в сети).
    // Идентификаторы I-Numbers очень эффективны, они специально разработаны
    // и оптимизированы для обработки сетевыми маршрутизаторами.
    //
    // I-Names — удобные для человека названия (похожие на домены системы DNS).
    // Как имена доменов DNS разрешаются DNS-серверами в IP-адреса,
    // так и I-Names будут разрешаться в I-Numbers. Но, в отличие
    // от I-Numbers, идентификаторы I-Names смогут быть переопределены
    // их владельцами для идентификации других ресурсов. Например,
    // если I-Name ассоциирован с названием компании, а компания решила
    // изменить своё название, то она может передать свой старый идентификатор
    // I-Name другой компании. Хотя, при этом, обе компании останутся
    // со своими старыми идентификаторами I-Numbers.
    //
    // Разработкой Спецификации XRI занимается Технический комитет XRI
    // (англ. XRI Technical Committee) в организации OASIS. Сама
    // организация OASIS занимается разработкой открытых стандартов
    // для деловой индустрии Интернета, т. н. E-Business.
    // Спецификация XRI 1.0 была опубликована в январе 2004 года.
    // В марте 2005 года вышла спецификация версии 2.0.
    // Сейчас идёт работа над версией 2.1.
    //
    // Примеры I-Names
    //
    // =Mary.Jones
    // @Jones.and.Company
    // +phone.number
    // +phone.number/(+area.code)
    // =Mary.Jones/(+phone.number)
    // @Jones.and.Company/(+phone.number)
    // @Jones.and.Company/((+phone.number)/(+area.code))
    //
    // Примеры I-Numbers
    //
    // !!1002!A7C5
    // !!1002!A7C5/!D90F.88
    //
    // В XRI допускается использование I-Names и I-Numbers вместе
    // в одном идентификаторе:
    //
    // !!1002!A745/(+phone.number)
    // @Jones.and.Company/!D90F.88/(+area.code)
    // Заметьте, что здесь не указан префикс xri://, который используется
    // для обозначения идентификаторов XRI как схемы URI.
    //

    /// <summary>
    /// Extensible Resource Identifier.
    /// </summary>
    [XmlRoot ("xri")]
    public sealed class Xri
        : IHandmadeSerializable,
        IVerifiable
    {
        #region Properties

        /// <summary>
        /// Идентификатор.
        /// </summary>
        [XmlElement ("identifier")]
        [JsonPropertyName ("identifier")]
        [Description ("Идентификатор")]
        [DisplayName ("Идентификатор")]
        public string? Identifier { get; set; }

        /// <summary>
        /// Произвольные пользовательские данные.
        /// </summary>
        [XmlIgnore]
        [JsonIgnore]
        [Browsable (false)]
        public object? UserData { get; set; }

        #endregion

        #region Public methods

        /// <summary>
        /// Разбор текстового представления.
        /// </summary>
        public void ParseText
            (
                ReadOnlySpan<char> text
            )
        {
            Sure.NotEmpty (text);

            var navigator = new ValueTextNavigator(text);
            Identifier = navigator.GetRemainingText().Trim().ThrowIfEmpty().ToString();
        }

        #endregion

        #region IHandmadeSerializable members

        /// <inheritdoc cref="IHandmadeSerializable.RestoreFromStream"/>
        public void RestoreFromStream
            (
                BinaryReader reader
            )
        {
            Sure.NotNull (reader);

            Identifier = reader.ReadNullableString();
        }

        /// <inheritdoc cref="IHandmadeSerializable.SaveToStream"/>
        public void SaveToStream
            (
                BinaryWriter writer
            )
        {
            Sure.NotNull (writer);

            writer
                .WriteNullable (Identifier);
        }

        #endregion

        #region IVerifiable members

        /// <inheritdoc cref="IVerifiable.Verify"/>
        public bool Verify
            (
                bool throwOnError
            )
        {
            var verifier = new Verifier<Xri> (this, throwOnError);

            verifier
                .NotNullNorEmpty (Identifier);

            return verifier.Result;
        }

        #endregion

        #region Object members

        /// <inheritdoc cref="object.ToString"/>
        public override string ToString()
        {
            return Identifier.ToVisibleString();
        }

        #endregion
    }
}
